package okw.communication;

import java.net.BindException;
import java.util.ArrayList;
import java.util.HashMap;

import okw.ModulesManager;

import org.apache.log4j.Logger;

import core.communication.Client;
import core.communication.ClientChecker;
import core.communication.Direction;
import core.database.model.NodeExtended;

public class CommunicationManager extends core.managers.CommunicationManager<NodeExtended, ModulesManager, MessageReceiver, MessageSender, InvocationHandler> {
	private static CommunicationManager instance = null;
	private HashMap<String, Client> topClients = new HashMap<>();
	private HashMap<String, Client> downClients = new HashMap<>();

	/**
	 * @return Instancja CommunicationManagera
	 */
	public static CommunicationManager getInstance() throws BindException {
		if (instance == null)
			synchronized (CommunicationManager.class) {
				if (instance == null)
					instance = new CommunicationManager();
			}
		return instance;
	}

	private CommunicationManager() throws BindException {
		logger = Logger.getLogger("DEBUG");
		modulesManager = ModulesManager.getInstance();
		initializeNodes();
		invocationHandler = new InvocationHandler(this);
		setupServer(invocationHandler);
		initializeMessages();
		initializeClientChecker();
	}

	@Override
	protected void initializeMessagsThreads() {
		messageReceiver = new MessageReceiver("receiver", this);
		messageSender = new MessageSender("sender", this);
	}
	
	@Override
	protected void initializeClientChecker() {
		clientChecker = new ClientChecker<CommunicationManager>("clientChceker", this);
	}

	@Override
	protected void initializeNodes() {
		this.mainNode = modulesManager.getMainNode();
		prefferedParentURI = mainNode.getPrefferedParentURI();
		ArrayList<NodeExtended> topNodes = modulesManager.getNodesForDirection(Direction.UP);
		for (NodeExtended node : topNodes)
			topClients.put(node.getLocatorURI(), new Client(node.getLocatorURI(), node.getNodeType()));
		ArrayList<NodeExtended> downNodes = modulesManager.getNodesForDirection(Direction.DOWN);
		for (NodeExtended node : downNodes)
			downClients.put(node.getLocatorURI(), new Client(node.getLocatorURI(), node.getNodeType()));
	}

	@Override
	public HashMap<String, Client> getClients(Direction direction) {
		if (direction == Direction.UP)
			return topClients;
		else
			return downClients;
	}

	@Override
	protected void addTopClient(String locatorUri, Client client) {
		topClients.put(locatorUri, client);
	}

	@Override
	protected void addDownClient(String locatorUri, Client client) {
		downClients.put(locatorUri, client);
	}

	@Override
	public void deleteClient(String locatorUri) {
		Client client = null;
		if (topClients.containsKey(locatorUri)) {
			client = topClients.get(locatorUri);
			topClients.remove(locatorUri);
		} else if (downClients.containsKey(locatorUri)) {
			client = downClients.get(locatorUri);
			downClients.remove(locatorUri);
		}
		if (client != null) {
			removeClientToCheck(client);
			client.getClient().disconnect();
		}
		modulesManager.deleteNode(locatorUri);
	}

	@Override
	public void editNode(String locatorUri, NodeExtended node) {
		if (locatorUri.compareTo(node.getLocatorURI()) != 0) {
			Client client = null;
			if (topClients.containsKey(locatorUri)) {
				client = topClients.get(locatorUri);
				client.getClient().disconnect();
				topClients.remove(locatorUri);
				topClients.put(node.getLocatorURI(), new Client(node.getLocatorURI(), node.getNodeType()));
			} else if (downClients.containsKey(locatorUri)) {
				client = downClients.get(locatorUri);
				client.getClient().disconnect();
				downClients.remove(locatorUri);
				downClients.put(node.getLocatorURI(), new Client(node.getLocatorURI(), node.getNodeType()));
			}
		}
		modulesManager.updateNode(locatorUri, node);
	}
}
